1 module hip.util.conv;
2 import hip.util.string;
3 import std.typecons;
4 import std.traits:isArray, isCallable;
5 public import hip.util.to_string_range;
6 public import hip.util.string:toStringz;
7 
8 
9 string toString(dstring dstr) pure nothrow @safe
10 {
11     try
12     {
13         string ret;
14         foreach(ch; dstr)
15             ret~= ch;
16         return ret;
17     }
18     catch(Exception e){return "";}
19 }
20 
21 string toString(char[] arr) pure nothrow @trusted @nogc {return cast(string)arr;}
22 string toString(T)(T[] arr) pure nothrow @safe if(!is(T == char))
23 {
24     string ret = "[";
25     for(int i = 0; i < arr.length; i++)
26     {
27         if(i)
28             ret~= ",";
29         ret~= toString(arr[i]);
30     }
31     return ret~"]";
32 }
33 
34 
35 string toString(T)(T structOrTupleOrEnum) pure nothrow @safe if(!isArray!T)
36 {
37     static if(isTuple!T)
38     {
39         alias tupl = structOrTupleOrEnum;
40         string ret;
41         foreach(i, v; tupl)
42         {
43             if(i > 0)
44                 ret~= ", ";
45             ret~= to!string(v);
46         }
47         return T.stringof~"("~ret~")";
48     }
49     else static if(is(T == struct))//For structs declaration
50     {
51         import hip.util.reflection;
52         static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format"))
53         {
54             import hip.util.format;
55             return formatFromType(structOrTupleOrEnum);
56         }
57         else
58         {
59             alias struct_ = structOrTupleOrEnum;
60             string s = "(";
61             static foreach(i, alias m; T.tupleof)
62             {
63                 if(i > 0)
64                     s~= ", ";
65                 s~= to!string(struct_.tupleof[i]);
66             }
67             return T.stringof~s~")";
68         }
69     }
70     else static if(is(T == enum))
71     {
72         foreach(mem; __traits(allMembers, T))
73             if(__traits(getMember, T, mem) == structOrTupleOrEnum)
74                 return T.stringof~"."~mem;
75         return T.stringof~".|MEMBER_NOT_FOUND|";
76     }
77     else static assert(0, "Not implemented for "~T.stringof);
78 }
79 
80 
81 
82 string dumpStructToString(T)(T struc) if(is(T == struct))
83 {
84     string s = "\n(";
85     static foreach(i, alias m; T.tupleof)
86     {
87         s~= "\n\t "~m.stringof~": ";
88         s~= toString(struc.tupleof[i]);
89     }
90     return T.stringof~s~"\n)";
91 }
92 
93 
94 T toStruct(T)(string struc) pure nothrow
95 {
96     T ret;
97     string[] each;
98     string currentArg;
99 
100     bool isInsideString = false;
101     for(size_t i = 1; i < (cast(int)struc.length)-1; i++)
102     {
103         if(!isInsideString && struc[i] == ',')
104         {
105             each~= currentArg;
106             currentArg = null;
107             if(struc[i+1] == ' ')
108             	i++;
109             continue;
110         }
111         else if(struc[i] == '"')
112         {
113             isInsideString = !isInsideString;
114             continue;
115         }
116         currentArg~= struc[i];
117     }
118     if(currentArg.length != 0)
119         each~=currentArg;
120 
121     static foreach(i, m; __traits(allMembers, T))
122     {{
123         alias member = __traits(getMember, ret, m);
124         member = to!(typeof(member))(each[i]);
125     }}
126     return ret;
127 }
128 
129 
130 bool toBool(string str) pure nothrow @safe @nogc {return str == "true";}
131 
132 ///Use that for making toStruct easier
133 string toString(string str) pure nothrow @safe @nogc {return str;}
134 
135 
136 string toString(bool b) pure nothrow @safe @nogc
137 {
138     return b ? "true" : "false";
139 }
140 
141 TO to(TO, FROM)(FROM f) pure nothrow
142 {
143     static if(is(TO == string))
144     {
145         static if(is(FROM == const(char)*) || is(FROM == char*))
146             return fromStringz(f);
147         else static if(is(FROM == enum))
148             return toString!FROM(f);
149         else
150             return toString(f);
151     }
152     else static if(is(TO == int))
153     {
154         static if(!is(FROM == string))
155             return toInt(f.toString);
156         else
157             return toInt(f);
158     }
159     else static if(is(TO == uint) || is(TO == size_t) || is(TO == ubyte) || is(TO == ushort))
160     {
161         static if(!is(FROM == string))
162             return cast(TO)toInt(f.toString);
163         else
164             return cast(TO)toInt(f);
165     }
166     else static if(is(TO == float))
167     {
168         static if(!is(FROM == string))
169             return toFloat(f.toString);
170         else
171             return toFloat(f);
172     }
173     else static if(is(TO == bool))
174     {
175         static if(!is(FROM == string))
176             return toBool(f.toString);
177         else
178             return toBool(f);
179     }
180     else
181     {
182         static if(!is(FROM == string))
183             return toStruct!TO(f.toString);
184         else
185             return toStruct!TO(f);
186     }
187 }
188 
189 /// This function can be called at compilation time without bringing runtime
190 string toString(long x) pure nothrow @safe
191 {
192     enum numbers = "0123456789";
193     bool isNegative = x < 0;
194     if(isNegative)
195         x*= -1;
196     size_t div = 10;
197     int length = 1;
198     int count = 1;
199     while(div <= x)
200     {
201         div*=10;
202         length++;
203     }
204     if(isNegative) length++;
205     char[] ret = new char[](length);
206     if(isNegative)
207         ret[0] = '-';
208     div = 10;
209     while(div <= x)
210     {
211         count++;
212         ret[length-count]=numbers[cast(size_t)((x/div)%10)];
213         div*=10;
214     }
215     ret[length-1] = numbers[cast(size_t)(x%10)];
216     return ret[0..$];
217 }
218 
219 
220 string toString(float f) pure nothrow @safe 
221 {
222     if(f != f) return "nan";
223     else if(f == -float.infinity) return "-inf";
224     else if(f == float.infinity) return "inf";
225 
226     bool isNegative = f < 0;
227     if(isNegative)
228         f = -f;
229     
230     float decimal = f - cast(int)f;
231     string ret = (cast(int)f).toString;
232     if(isNegative)
233         ret = "-"~ret;
234     
235     if(decimal == 0)
236         return ret;
237     ret~= '.';
238     long multiplier = 10;
239     while(cast(long)(decimal*multiplier) < (decimal*multiplier))
240     {
241         if(cast(long)(decimal*multiplier) == 0)
242         	ret~= '0';
243         multiplier*=10;
244     }
245     return ret ~ (cast(long)(decimal*multiplier)).toString;
246 }
247 
248 pure string toString(void* ptr) @safe nothrow
249 {
250     return ptr is null ? "null" : toHex(cast(size_t)ptr);
251 }
252 
253 
254 pure string toHex(size_t n) @safe nothrow
255 {
256     enum numbers = "0123456789ABCDEF";
257     int preAllocSize = 1;
258     ulong div = 16;
259     while(div <= n)
260     {
261         div*= 16;
262         preAllocSize++;
263     }
264     div/= 16;
265     char[] ret = new char[](preAllocSize);
266     int i = 0;
267 
268     while(div >= 16)
269     {
270         ret[i++] = numbers[(n/div)%16];
271         div/= 16;
272     }
273     ret[i] = numbers[n%16];
274     return ret[0..$];
275 }
276 
277 
278 string fromUTF16(wstring str) pure nothrow
279 {
280     string ret;
281     foreach(c;str) ret~= c;
282     return ret;
283 }
284 
285 int toInt(string str) pure nothrow @safe @nogc
286 {
287     if(str.length == 0) return 0;
288     str = str.trim;
289 
290     int i = (cast(int)str.length)-1;
291 
292     int last = 0;
293     int multiplier = 1;
294     int ret = 0;
295     if(str[0] == '-')
296     {
297         last++;
298         multiplier*= -1;
299     }
300     for(; i >= last; i--)
301     {
302         if(str[i] >= '0' && str[i] <= '9')
303             ret+= (str[i] - '0') * multiplier;
304         else
305             return ret;
306         multiplier*= 10;
307     }
308     return ret;
309 }
310 
311 
312 float toFloat(string str) pure nothrow @safe @nogc
313 {
314     if(str.length == 0) return 0;
315     str = str.trim;
316     if(str == "nan" || str == "NaN") return float.init;
317     if(str == "inf" || str == "infinity" || str == "Infinity") return float.infinity;
318 
319     int integerPart = 0;
320     int decimalPart = 0;
321     int i = 0;    
322     bool isNegative = str[0] == '-';
323     if(isNegative)
324         str = str[1..$];
325     bool isDecimal = false;
326     for(; i < str.length; i++)
327     {
328         if(str[i] =='.')
329         {
330             isDecimal = true;
331             continue;
332         }
333         if(isDecimal)
334             decimalPart++;
335         else
336             integerPart++;
337     }
338     
339     if(decimalPart == 0)
340         return (isNegative ? -1 : 1) * cast(float)str.toInt;
341 
342     i = 0;
343     float decimal= 0;
344     float integer  = 0;
345     int integerMultiplier = 1;
346     float floatMultiplier = 1.0f/10.0f;
347 
348     while(integerPart > 0)
349     {
350         //Iterate the number from backwards towards the greatest value
351         integer+= (str[integerPart - 1] - '0') * integerMultiplier;
352         integerMultiplier*= 10;
353         integerPart--;
354         i++;
355     }
356     i++; //Jump the .
357     while(decimalPart > 0)
358     {
359         decimal+= (str[i] - '0') * floatMultiplier;
360         floatMultiplier/= 10;
361         decimalPart--;
362         i++;
363     }
364     return (integer + decimal) * (isNegative ? -1 : 1);
365 }
366 
367 
368 unittest
369 {
370     assert(toString(500) == "500");
371     assert(toFloat("50.5") == 50.5);
372     assert(toString(100.0) == "100");
373     assert(toInt("-500") == -500);
374     assert(toString("Hello") == "Hello");
375     assert(toString(true) == "true");
376     assert(toString(50.25)== "50.25");
377     assert(toString(0.003) == "0.003");
378     assert(toString(0.999) == "0.999");
379 }